/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.netplay.server;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import sirtet.netplay.NetworkPlayer;

/**
 * A community that holds different kinds of GameRooms.  By default this
 * community is AUTO, meaning the arriving player will be automatically
 * assigned to a game room (one will be created if no room is currently
 * available).
 * @author guangwei.zhu
 */
public class GameCommunity extends ServerObject {

    private Set<GameRoom> rooms = new HashSet<GameRoom>();
    private Map<NetworkPlayer, PlayerStatus> roster = new HashMap<NetworkPlayer, PlayerStatus>();
    private Set<GameRoomFactory> factories = new HashSet<GameRoomFactory>();

    private class PlayerStatus {

        public PlayerStatus(NetworkPlayer player) {
            this.player = player;
        }
        public final NetworkPlayer player;
        public GameRoom currentRoom;
    }

    /**
     * Triggered after the newly arriving player if registered in the community.
     * This method can be overridden to handle player in a way other than
     * automatic room assignment.
     * @param player the arriving player
     */
    protected synchronized void handlePlayer(NetworkPlayer player) {
        // Automatically try to participate games in existing rooms
        for (GameRoom room : rooms) {
            if (room.participate(player)) {
                // Successfully joined in this room
                roster.get(player).currentRoom = room;
                return;
            }
        }

        GameRoom room = this.createRoom(player.gameType());
        if (room != null) {
            rooms.add(room);
            if (room.participate(player)) {
                roster.get(player).currentRoom = room;
                return;
            }
        }
        // TODO if no suitable room can be created for this player
        throw new IllegalArgumentException("No room is available.");
    }

    protected GameRoom createRoom(String typeString) {
        for (GameRoomFactory factory : factories) {
            if (factory.getTypeString().equals(typeString)) {
                return factory.createRoom();
            }
        }
        return null;
    }

    /**
     * Handles the event of a new arriving player
     * @param player the newly arrived NetworkPlayer object
     */
    @Override
    protected synchronized void playerArrived(NetworkPlayer player) {
        if (roster.containsKey(player)) {
            return;     // Already in the community
        }

        this.registerPlayer(player);    // Register player
        this.handlePlayer(player);      // Handle arriving player
    }

    /**
     * Register the NetworkPlayer object, which create handlers to capture
     * more network messages.
     * @param player the NetworkPlayer who wants to join the community
     */
    public synchronized void registerPlayer(NetworkPlayer player) {
        if (roster.containsKey(player)) {
            return;     // Already registered
        }

        PlayerStatus status = new PlayerStatus(player);
        roster.put(player, status);       // Null register
        status.currentRoom = null;        // Roaming
    }

    /**
     * Unregister a player and disconnect event handlers by this community
     * @param player the NetworkPlayer who wants to leave the community
     */
    public synchronized void unregisterPlayer(NetworkPlayer player) {
        if (!roster.containsKey(player)) {
            return;     // Not registered
        }

        PlayerStatus status = roster.get(player);
        roster.remove(player);
    }

    /**
     * Register a factory which can create a new room upon request.
     * @param factory a concrete GameRoomFactory object
     */
    public void registerFactory(GameRoomFactory factory) {
        for (GameRoomFactory other : factories) {
            if (other.equals(factory)) {
                // If the factory is already registered
                return;
            }
            if (other.getTypeString().equals(factory.getTypeString())) {
                // There is a confliction in type strings
                throw new IllegalArgumentException("Type string conflict is detected.");
            }
        }

        factories.add(factory);
    }

    /**
     * Unregister a previously registered factory.
     * @param factory a concrete GameRoomFactory object
     */
    public void unregisterFactory(GameRoomFactory factory) {
        factories.remove(factory);
    }
}
